<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GameBoard 单元测试</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .test-container {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        button {
            background: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            margin: 10px 0;
        }
        button:hover {
            background: #0056b3;
        }
        #test-output {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            padding: 15px;
            margin-top: 20px;
            font-family: monospace;
            white-space: pre-wrap;
            max-height: 400px;
            overflow-y: auto;
        }
    </style>
</head>
<body>
    <div class="test-container">
        <h1>GameBoard 单元测试</h1>
        <p>点击下面的按钮运行GameBoard类的单元测试</p>
        
        <button onclick="runTests()">运行测试</button>
        <button onclick="clearOutput()">清空输出</button>
        
        <div id="test-output"></div>
    </div>

    <script type="module">
        import { GameBoard } from './js/GameBoard.js';

        // 简单的测试框架
        class TestRunner {
            constructor() {
                this.tests = [];
                this.passed = 0;
                this.failed = 0;
                this.output = [];
            }

            test(name, testFn) {
                this.tests.push({ name, testFn });
            }

            run() {
                this.output = [];
                this.passed = 0;
                this.failed = 0;
                
                this.log('开始运行GameBoard测试...\n');
                
                this.tests.forEach(({ name, testFn }) => {
                    try {
                        testFn();
                        this.log(`✅ ${name}`);
                        this.passed++;
                    } catch (error) {
                        this.log(`❌ ${name}: ${error.message}`);
                        this.failed++;
                    }
                });

                this.log(`\n测试完成: ${this.passed} 通过, ${this.failed} 失败`);
                
                // 显示结果
                document.getElementById('test-output').textContent = this.output.join('\n');
            }

            log(message) {
                this.output.push(message);
                console.log(message);
            }

            assert(condition, message) {
                if (!condition) {
                    throw new Error(message || '断言失败');
                }
            }

            assertEqual(actual, expected, message) {
                if (actual !== expected) {
                    throw new Error(message || `期望 ${expected}, 实际 ${actual}`);
                }
            }

            assertNotNull(value, message) {
                if (value === null || value === undefined) {
                    throw new Error(message || '值不应为null或undefined');
                }
            }
        }

        const runner = new TestRunner();

        // 测试网格初始化
        runner.test('网格初始化测试', () => {
            const board = new GameBoard();
            
            runner.assertEqual(board.width, 8, '网格宽度应为8');
            runner.assertEqual(board.height, 8, '网格高度应为8');
            runner.assertNotNull(board.grid, '网格应被初始化');
            
            // 检查所有单元格都被创建
            for (let x = 0; x < 8; x++) {
                for (let y = 0; y < 8; y++) {
                    const cell = board.getCell(x, y);
                    runner.assertNotNull(cell, `单元格 (${x},${y}) 应存在`);
                    runner.assert(cell.type >= 0 && cell.type <= 4, `单元格类型应在0-4范围内`);
                    runner.assertEqual(cell.x, x, '单元格X坐标应正确');
                    runner.assertEqual(cell.y, y, '单元格Y坐标应正确');
                }
            }
        });

        // 测试单元格操作
        runner.test('单元格操作测试', () => {
            const board = new GameBoard();
            
            // 测试getCell
            const cell = board.getCell(3, 4);
            runner.assertNotNull(cell, '应能获取有效位置的单元格');
            
            // 测试边界检查
            runner.assertEqual(board.getCell(-1, 0), null, '负坐标应返回null');
            runner.assertEqual(board.getCell(8, 0), null, '超出边界应返回null');
            runner.assertEqual(board.getCell(0, 8), null, '超出边界应返回null');
            
            // 测试setCellType
            runner.assert(board.setCellType(3, 4, 2), '应能设置有效位置的单元格类型');
            runner.assertEqual(board.getCell(3, 4).type, 2, '单元格类型应被正确设置');
            
            // 测试isValidPosition
            runner.assert(board.isValidPosition(0, 0), '(0,0)应是有效位置');
            runner.assert(board.isValidPosition(7, 7), '(7,7)应是有效位置');
            runner.assert(!board.isValidPosition(-1, 0), '(-1,0)应是无效位置');
            runner.assert(!board.isValidPosition(8, 0), '(8,0)应是无效位置');
        });

        // 测试相邻检测
        runner.test('相邻检测测试', () => {
            const board = new GameBoard();
            
            // 测试水平相邻
            runner.assert(board.areAdjacent({x: 3, y: 4}, {x: 4, y: 4}), '水平相邻应返回true');
            runner.assert(board.areAdjacent({x: 4, y: 4}, {x: 3, y: 4}), '水平相邻（反向）应返回true');
            
            // 测试垂直相邻
            runner.assert(board.areAdjacent({x: 3, y: 4}, {x: 3, y: 5}), '垂直相邻应返回true');
            runner.assert(board.areAdjacent({x: 3, y: 5}, {x: 3, y: 4}), '垂直相邻（反向）应返回true');
            
            // 测试非相邻
            runner.assert(!board.areAdjacent({x: 3, y: 4}, {x: 5, y: 4}), '距离2的位置不应相邻');
            runner.assert(!board.areAdjacent({x: 3, y: 4}, {x: 4, y: 5}), '对角线位置不应相邻');
            runner.assert(!board.areAdjacent({x: 3, y: 4}, {x: 3, y: 4}), '相同位置不应相邻');
        });

        // 测试交换功能
        runner.test('单元格交换测试', () => {
            const board = new GameBoard();
            
            // 设置测试数据
            board.setCellType(2, 3, 1);
            board.setCellType(2, 4, 3);
            
            const originalType1 = board.getCell(2, 3).type;
            const originalType2 = board.getCell(2, 4).type;
            
            // 执行交换
            const success = board.swapCells({x: 2, y: 3}, {x: 2, y: 4});
            
            runner.assert(success, '交换应成功');
            runner.assertEqual(board.getCell(2, 3).type, originalType2, '第一个单元格类型应被交换');
            runner.assertEqual(board.getCell(2, 4).type, originalType1, '第二个单元格类型应被交换');
        });

        // 测试匹配检测
        runner.test('水平匹配检测测试', () => {
            const board = new GameBoard();
            
            // 创建水平匹配
            board.setCellType(2, 3, 1);
            board.setCellType(3, 3, 1);
            board.setCellType(4, 3, 1);
            
            const matches = board.findHorizontalMatches();
            
            runner.assert(matches.length > 0, '应检测到水平匹配');
            
            const match = matches.find(m => 
                m.cells.some(cell => cell.x === 2 && cell.y === 3) &&
                m.cells.some(cell => cell.x === 3 && cell.y === 3) &&
                m.cells.some(cell => cell.x === 4 && cell.y === 3)
            );
            
            runner.assertNotNull(match, '应找到正确的水平匹配');
            runner.assertEqual(match.direction, 'horizontal', '匹配方向应为水平');
            runner.assertEqual(match.type, 1, '匹配类型应正确');
            runner.assert(match.length >= 3, '匹配长度应至少为3');
        });

        // 测试垂直匹配检测
        runner.test('垂直匹配检测测试', () => {
            const board = new GameBoard();
            
            // 创建垂直匹配
            board.setCellType(3, 2, 2);
            board.setCellType(3, 3, 2);
            board.setCellType(3, 4, 2);
            
            const matches = board.findVerticalMatches();
            
            runner.assert(matches.length > 0, '应检测到垂直匹配');
            
            const match = matches.find(m => 
                m.cells.some(cell => cell.x === 3 && cell.y === 2) &&
                m.cells.some(cell => cell.x === 3 && cell.y === 3) &&
                m.cells.some(cell => cell.x === 3 && cell.y === 4)
            );
            
            runner.assertNotNull(match, '应找到正确的垂直匹配');
            runner.assertEqual(match.direction, 'vertical', '匹配方向应为垂直');
            runner.assertEqual(match.type, 2, '匹配类型应正确');
            runner.assert(match.length >= 3, '匹配长度应至少为3');
        });

        // 测试重力效果
        runner.test('重力效果测试', () => {
            const board = new GameBoard();
            
            // 创建一些空单元格
            board.clearCell(3, 5);
            board.clearCell(3, 6);
            
            // 记录上方单元格的类型
            const topCellType = board.getCell(3, 4).type;
            
            // 应用重力
            const moves = board.applyGravity();
            
            runner.assert(moves.length > 0, '应产生移动');
            
            // 检查单元格是否正确下落
            runner.assertEqual(board.getCell(3, 7).type, topCellType, '单元格应下落到底部');
            runner.assert(board.isEmpty(3, 4), '原位置应变为空');
        });

        // 测试随机生成的正确性
        runner.test('随机生成测试', () => {
            const board = new GameBoard();
            
            // 检查是否使用了不同类型
            const typesUsed = new Set();
            for (let x = 0; x < 8; x++) {
                for (let y = 0; y < 8; y++) {
                    const cell = board.getCell(x, y);
                    if (cell.type >= 0) {
                        typesUsed.add(cell.type);
                    }
                }
            }
            
            runner.assert(typesUsed.size >= 3, '应使用至少3种不同类型的小鬼');
            
            // 检查类型范围
            for (const type of typesUsed) {
                runner.assert(type >= 0 && type <= 4, '所有类型应在0-4范围内');
            }
            
            // 检查初始状态无匹配
            const initialMatches = board.findMatches();
            runner.assertEqual(initialMatches.length, 0, '初始状态不应有匹配');
        });

        // 暴露到全局
        window.runTests = () => runner.run();
        window.clearOutput = () => {
            document.getElementById('test-output').textContent = '';
        };
    </script>
</body>
</html>